import {stringUtils} from "../utils/stringUtils";
import {ScriptRender} from "../utils/ScriptRender";
import {mergeToMidware} from "./midware";
import jQuery from "jquery";

let extendModuleContexts = {};

const extendModuleRequire = function (key, context) {
    let defaultCtx = '_';
    if (typeof context === 'string') {
        defaultCtx = context;
    }
    let ctx = getOwn(extendModuleContexts, defaultCtx);
    if (!ctx) {
        throw new Error('context not exists');
    }
    ctx.load(key);
    return extendModuleContexts[defaultCtx].getDef(key);
};

const extendModuleRelease = function (key, context) {
    let defCtx = '_';
    if (typeof context === 'string') {
        defCtx = context;
    }
    let ctx = getOwn(extendModuleContexts, defCtx);
    if (!ctx) {
        throw new Error('context not exists');
    }
    ctx.unload(key);
};

const extendModuleDefine = function (key, main, context, scope) {
    let defaultContext = '_';
    if (typeof context === 'string' && !main) {
        defaultContext = context;
    }
    if (typeof main !== 'function' && typeof context === 'function') {
        main = context;
    }
    let ctx = getOwn(extendModuleContexts, defaultContext);
    if (ctx) {
        ctx.pushQueue(key, main, scope);
    }
};

const EXTEND_MODULE_TYPE = {
    ExtendModule: 'ExtendModule',
    StyleResource: 'StyleResource',
    MaterialResource: 'MaterialResource'
};

const extendModule = {
    config: function (cfg) {
        if (cfg.context) {
            let existingContext = getOwn(extendModuleContexts, cfg.context);
            if (existingContext) {
                //reConfig existing context
                existingContext.config(cfg);
            } else {
                //config new context
                extendModuleContexts[cfg.context] = new ExtendModuleContext(cfg.context, cfg);
            }
            return cfg.context;
        }
        throw new Error('')
    },
    getBasePath: function (context = '_') {
        let existingContext = getOwn(extendModuleContexts, context);
        if (existingContext) {
            return extendModuleContexts[context].Config.baseUrl;
        }
        return undefined;
    },
    isBrowser: function () {
        return typeof document !== 'undefined' && typeof navigator !== 'undefined';
    }
};

class ExtendModuleContext {
    constructor(name, config) {
        this._name = name;
        this._defaultConfig = {
            waitSeconds: 7,
            baseUrl: './'
        };
        if (config) {
            this._config = jQuery.extend(true, this._defaultConfig, config);
        }
        this._extendConfig = {};
        this._deferreds = {};
        this._requires = {};
        this._queue = [];
        this._cache = {};
    }

    get Config() {
        return this._config;
    }

    config(cfg) {
        if (cfg && cfg.context === this._name) {
            this._name = cfg.context;
            jQuery.extend(true, this._config, cfg);
        }
    }

    /**
     * 加载扩展模块
     * @param key 扩展模块的键值
     */
    load(key) {
        //构建延迟对象
        this._makeDef(key);
        //扩展模块配置
        let extendConfig = null;
        this._getModuleConfig(key)
            .done((data) => {
                if (data.config) {
                    extendConfig = data.config;
                } else {
                    extendConfig = data;
                    this._extendConfig[key] = extendConfig;
                }

                switch (extendConfig.type) {
                    case EXTEND_MODULE_TYPE.ExtendModule:
                        //扩展模块处理main和style配置
                        if (extendConfig.style) {
                            //加载样式
                            let styleUrl = stringUtils.urlJoin(this._config.baseUrl, key, extendConfig.style);
                            if (!ScriptRender.checkStyle(styleUrl)) {
                                ScriptRender.addStyle(styleUrl);
                            }
                        }
                        if (extendConfig.main) {
                            //如果是还没有加载过的
                            if (!hasProp(this._cache, key)) {
                                //加载主模块
                                let mainUrl = stringUtils.urlJoin(this._config.baseUrl, key, extendConfig.main);
                                this._loadScript({
                                    id: key,
                                    url: mainUrl
                                });
                            } else {
                                let cachedClazz = getOwn(this._cache, key);
                                this._deferreds[key].resolve(cachedClazz);
                            }
                        }
                        delete this._requires[key];
                        break;
                    case EXTEND_MODULE_TYPE.StyleResource:
                        //样式资源
                        if (extendConfig.files && Array.isArray(extendConfig.files)) {
                            extendConfig.files.forEach((item) => {
                                let styleUrl = stringUtils.urlJoin(this._config.baseUrl, key, item);
                                if (!ScriptRender.checkStyle(styleUrl)) {
                                    ScriptRender.addStyle(styleUrl);
                                }
                            });
                            this._deferreds[key].resolve();
                        } else {
                            this._deferreds[key].reject();
                        }
                        break;
                    case EXTEND_MODULE_TYPE.MaterialResource:
                        //素材资源
                        if (extendConfig.files && Array.isArray(extendConfig.files)) {
                            let realUrls = [];
                            extendConfig.files.forEach((item) => {
                                realUrls.push(stringUtils.urlJoin(this._config.baseUrl, key, item));
                            });
                            this._deferreds[key].resolve(realUrls);
                        } else {
                            this._deferreds[key].reject();
                        }
                        break;
                }
            });
    }

    unload(key) {
        let config = getOwn(this._extendConfig, key);
        if (!config) {
            throw new Error('can not found key in current context');
        }
        switch (config.type) {
            //处理扩展模块
            case EXTEND_MODULE_TYPE.ExtendModule:
                if (config.style) {
                    let url = stringUtils.urlJoin(this._config.baseUrl, key, config.style);
                    ScriptRender.removeStyle(url);
                }
                break;
            //处理样式资源
            case EXTEND_MODULE_TYPE.StyleResource:
                if (config.files && Array.isArray(extendConfig.files)) {
                    config.files.forEach((item) => {
                        let url = stringUtils.urlJoin(this._config.baseUrl, key, item);
                        ScriptRender.removeStyle(url);
                    });
                }
                break;
        }
    }

    getDef(key) {
        let own = hasProp(this._deferreds, key);
        if (own) {
            return this._deferreds[key].promise;
        }
        return undefined;
    }

    pushQueue(key, main, scope) {
        if (_.findIndex(this._queue, ['key', key]) > 0) {
            return;
        }
        this._queue.push({key: key, main: main, scope: scope});
    }

    _makeDef(key) {
        //创建延迟对象
        let def = {};
        def.promise = new Promise(function (resolve, reject) {
            def.resolve = resolve;
            def.reject = function (err) {
                reject(err);
            }
        });
        if (hasProp(this._deferreds, key)) {
            delete this._deferreds[key];
        }
        this._deferreds[key] = def;
    }

    _loadScript(map) {
        let script = document.createElement('script');
        script.setAttribute('data-extend-module', map.id);
        script.type = 'text/javascript';
        script.charset = 'utf-8';
        script.async = true;
        script.src = map.url;
        script.addEventListener('load', this._onScriptLoaded.bind(this), false);
        script.addEventListener('error', this._onScriptError.bind(this), false);
        document.head.appendChild(script);
    }

    _getModuleConfig(key) {
        if (this._extendConfig[key]) {
            return jQuery.Deferred().resolve({config: this._extendConfig[key]});
        } else {
            const path = stringUtils.urlJoin(this._config.baseUrl, key, 'package.json');
            return jQuery.getJSON(path);
        }
    }

    _onScriptError() {

    }

    _onScriptLoaded(e) {
        let key = e.target.dataset.extendModule;
        let main = undefined;
        if (hasProp(this._deferreds, key)) {
            let options = this._extendConfig[key].options || {};
            let fn = _.find(this._queue, ['key', key]);
            if (!fn) {
                this._deferreds[key].resolve(null);
            } else {
                main = fn.main.apply(fn.scope || window, [options]);
                if (!hasProp(this._cache, key)) {
                    //缓存当前的类定义
                    this._cache[key] = {clazz: main, config: options};
                }
                this._deferreds[key].resolve(this._cache[key]);
            }
        }
    }
}

function hasProp(obj, prop) {
    return Object.prototype.hasOwnProperty.call(obj, prop);
}

function getOwn(obj, prop) {
    return obj && hasProp(obj, prop) && obj[prop];
}

export function initAmoeba() {
    mergeToMidware('extendModule', extendModule);
    mergeToMidware('extendModuleRequire', extendModuleRequire);
    mergeToMidware('extendModuleDefine', extendModuleDefine);
    mergeToMidware('extendModuleRelease', extendModuleRelease);
}
